// Kanrong Shan
#ifndef __H_HTTP_CLIENT__
#define __H_HTTP_CLIENT__

#include <tstring.h>
#include <Wininet.h>
#pragma comment(lib, "Wininet.lib")
#include <scoped_ptr.h>
#include <Path.h>
#include <Directory.h>
#include <file.h>

namespace bl{



// Help to close WinINet handle
class WinInetHandle
{
public:
	WinInetHandle() : m_handle(NULL) {}
	WinInetHandle(HINTERNET handle) : m_handle(NULL) {Attach(handle);}
	~WinInetHandle() { Close(); }

	void Attach(HINTERNET handle)
	{
		ATLASSERT(m_handle==NULL);
		m_handle = handle;
	}

	HINTERNET Detach(){HANDLE h; h=m_handle; m_handle=NULL; return h;}

	void operator =(HINTERNET handle) {Attach(handle);}
	operator HINTERNET() const {return m_handle;}

	bool Close()
	{
		HINTERNET handle = m_handle;
		m_handle = NULL;
		if(handle!=NULL)
			return TRUE == InternetCloseHandle(handle);
		return false;
	}

	HINTERNET m_handle;

private:
	WinInetHandle( const WinInetHandle& handle);
	void operator=( const WinInetHandle& h );
};

class Url
{
public:
	enum SchemeType {HTTP, HTTPS};

	Url(const bl::tstring& url)
	{
		Open(url);
	}

	void Open(const bl::tstring& url)
	{
		URL_COMPONENTS comp;
		ZeroMemory(&comp, sizeof(URL_COMPONENTS));
		comp.dwStructSize = sizeof(URL_COMPONENTS);

		scoped_array<bl::tchar> scheme(new bl::tchar[url.length()]);
		scoped_array<bl::tchar> hostName(new bl::tchar[url.length()]);
		scoped_array<bl::tchar> path(new bl::tchar[url.length()]);

		comp.lpszScheme = scheme.get();
		comp.dwSchemeLength = url.length();
		comp.lpszHostName = hostName.get();
		comp.dwHostNameLength = url.length();
		comp.lpszUrlPath = path.get();
		comp.dwUrlPathLength = url.length();

		InternetCrackUrl(url.c_str(), 0, 0, &comp);

		scheme_.assign(scheme.get(), comp.dwSchemeLength);
		host_.assign(hostName.get(), comp.dwHostNameLength);
		path_.assign(path.get(), comp.dwUrlPathLength);
		port_ = comp.nPort;
		if(comp.nScheme == INTERNET_SCHEME_HTTP)
			scheme_type_ = HTTP;
		else if(comp.nScheme == INTERNET_SCHEME_HTTPS)
			scheme_type_ = HTTPS;
	}

	const bl::tstring& Scheme() const
	{
		return scheme_;
	}

	SchemeType GetSchemeType() const
	{
		return scheme_type_;
	}

	const bl::tstring& GetHost() const
	{
		return host_;
	}

	const bl::tstring& GetPath() const
	{
		return path_;
	}

	unsigned short GetPort() const
	{
		return port_;
	}

private:
	bl::tstring scheme_;
	bl::tstring host_;
	bl::tstring path_;
	unsigned short port_;
	SchemeType scheme_type_;
};


class HttpClient
{
public:
	static bool PostXML(const tstring& serverPath, const std::string& utf8Xml, std::string& response)
	{
		Url url(serverPath);

		WinInetHandle hInternet = InternetOpen(TEXT("Simple Http Client"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
		if(hInternet == NULL)
			return false;

		WinInetHandle hConn = InternetConnect(hInternet, url.GetHost().c_str(), url.GetSchemeType() == Url::HTTP ? INTERNET_DEFAULT_HTTP_PORT
			: INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
		if(hConn == NULL)
			return false;

		WinInetHandle hReq = HttpOpenRequest(hConn, TEXT("POST"), url.GetPath().c_str(), NULL, NULL, NULL, 
			url.GetSchemeType() == Url::HTTPS ? (INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_CACHE_WRITE) : INTERNET_FLAG_NO_CACHE_WRITE
			, NULL);
		if(hReq == NULL)
			return false;

		bl::tstring header;
		header = TEXT("Content-type:text/xml; charset=utf-8\r\n");
		header += TEXT("Accept: text/xml");
		if( !HttpSendRequest(hReq, header.c_str(), header.length(), (void*)utf8Xml.c_str(), utf8Xml.length()) )
			return false;
		
		char buffer[1024*10+1];
		unsigned long bytesRead;
		do 
		{
			if( !InternetReadFile(hReq, buffer, 1024*10, &bytesRead) )
				break;
			buffer[bytesRead] = 0;
			response += buffer;
		} while (bytesRead > 0);

		return true;
	}

	static bool DownloadFile(const tstring& fileUrl, const tstring& localFile)
	{
		tstring path = Path(localFile).GetParent();
		if(!Directory::Exist(path))
			Directory::CreateDirectory(path);

		WinInetHandle hInternet = InternetOpen(TEXT("Simple Http Client"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
		if(hInternet == NULL)
			return false;

		WinInetHandle hReq = InternetOpenUrl(hInternet, fileUrl.c_str(), TEXT(""), -1, INTERNET_FLAG_KEEP_CONNECTION, NULL);
		if(hReq == NULL)
			return false;

		DWORD statusCode;
		DWORD bufferLen = sizeof(DWORD);
		if( HttpQueryInfo(hReq, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (void*)&statusCode, &bufferLen, 0) )
		{
			if(statusCode == 404)
			{
				return false;
			}
		}

		File file;
		if( !file.OpenWrite(localFile.c_str()) )
			return false;

		InternetSetFilePointer(hReq, 0, NULL, FILE_BEGIN, 0);

		bool downloadOK = true;
		scoped_array<unsigned char> buff(new unsigned char[200*1024]);
		unsigned long bytesRead;
		do 
		{
			if( !InternetReadFile(hReq, buff.get(), 200*1024, &bytesRead) )
			{
				downloadOK = false;
				break;
			}
			
			if(bytesRead > 0)
			{
				if(!file.Write(buff.get(), bytesRead, NULL))
				{
					downloadOK = false;
					break;
				}
			}
		} while (bytesRead > 0);

		file.Close();

		if(!downloadOK)
		{
			File::Delete(localFile.c_str());
			return false;
		}

		return true;
	}
};

}

#endif